package com.nuanshui.heatedloan.middleware.async;

import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

import org.springframework.util.Assert;

import com.nuanshui.heatedloan.middleware.async.constant.AsyncConstant;
import com.nuanshui.heatedloan.middleware.async.core.AsyncExecutor;
import com.nuanshui.heatedloan.middleware.async.core.AsyncFuture;
import com.nuanshui.heatedloan.middleware.async.core.AsyncFutureCallback;
import com.nuanshui.heatedloan.middleware.async.core.AsyncTaskFuture;
import com.nuanshui.heatedloan.middleware.async.exception.AsyncException;
import com.nuanshui.heatedloan.middleware.async.pool.AsyncFutureTask;
import com.nuanshui.heatedloan.middleware.async.pool.AsyncPoolCallable;
import com.nuanshui.heatedloan.middleware.async.pool.AsyncRunnable;
import com.nuanshui.heatedloan.middleware.async.proxy.AsyncMethodProxy;
import com.nuanshui.heatedloan.middleware.async.proxy.AsyncProxy;
import com.nuanshui.heatedloan.middleware.async.proxy.AsyncResultProxy;
import com.nuanshui.heatedloan.middleware.async.util.ReflectionHelper;


/**
 * 编程式异步调用
 * @param 
 * @author guohao
 * @date 2016年12月5日 下午3:58:55
 * @since $
 */
@SuppressWarnings("all")
public class AsyncTemplate {

	private static AsyncProxy cglibProxy = new AsyncMethodProxy();

	public enum ProxyType {
		CGLIB
	}

	/**
	 * 获取代理方式：</br> ProxyType.CGLIB 返回Cglib代理
	 * @param @param type
	 * @param @return
	 * @author guohao
	 * @date 2016年12月5日 下午4:22:07
	 * @since $
	 */
	public static AsyncProxy getAsyncProxy(ProxyType type) {
		return cglibProxy;
	}

	/**
	 * 
	 * * @return T
	 *         必须带有返回参数且不支持void,array及Integer,Long,String,Boolean等Final修饰类</br>
	 *         如果需要返回以上类型，可以创建对象包装；
	 * @param @param t
	 * @param @return
	 * @author guohao
	 * @date 2016年12月5日 下午4:39:15
	 * @since $
	 */
	public static <T> T buildProxy(T t) {
		return buildProxy(t, 0);
	}

	/**
	 * 构建代理类
	 *
	 * </p>
	 * 
	 * @param t
	 *            需要被代理的类
	 * @param timeout
	 *            超时时间（单位：毫秒）
	 * @return T
	 *         必须带有返回参数且不支持void,array及Integer,Long,String,Boolean等Final修饰类</br>
	 *         如果需要返回以上类型，可以创建对象包装；如：
	 *         {@linkplain com.nuanshui.heatedloan.middleware.async.bean.AsyncResult}
	 * 
	 * @param @param t
	 * @param @param timeout
	 * @param @return
	 * @author guohao
	 * @date 2016年12月5日 下午4:44:23
	 * @since $
	 */
	public static <T> T buildProxy(T t, long timeout) {
		return (T) getAsyncProxy(ProxyType.CGLIB).buildProxy(t, timeout, true);
	}

	/**
	 * <p>
	 * 
	 * 构建代理类
	 *
	 * </p>
	 * 
	 * @param T
	 *            需要被代理的类
	 * @param proxyType
	 *            代理类型
	 * @return T
	 *         必须带有返回参数且不支持void,array及Integer,Long,String,Boolean等Final修饰类</br>
	 *         如果需要返回以上类型，可以创建对象包装；如：
	 *         {@linkplain com.nuanshui.heatedloan.middleware.async.bean.AsyncResult}
	 * 
	 * @param @param t
	 * @param @param proxyType
	 * @param @return
	 * @author guohao
	 * @date 2016年12月5日 下午4:44:39
	 * @since $
	 */
	public static <T> T buildProxy(T t, ProxyType proxyType) {
		return (T) getAsyncProxy(proxyType).buildProxy(t,
				AsyncConstant.ASYNC_DEFAULT_TIME_OUT, true);
	}

	/**
	 * 
	 * 
	 * * <p>
	 * 
	 * 构建代理类
	 *
	 * </p>
	 * 
	 * @param T
	 *            需要被代理的类
	 * @param timeout
	 *            超时时间（单位：毫秒）
	 * @param proxyType
	 *            代理类型
	 * @return T
	 *         必须带有返回参数且不支持void,array及Integer,Long,String,Boolean等Final修饰类</br>
	 *         如果需要返回以上类型，可以创建对象包装；如：
	 *         {@linkplain com.nuanshui.heatedloan.middleware.async.bean.AsyncResult}
	 * 
	 * 
	 * @param @param t
	 * @param @param timeout
	 * @param @param proxyType
	 * @param @return
	 * @author guohao
	 * @date 2016年12月5日 下午4:45:34
	 * @since $
	 */
	public static <T> T buildProxy(T t, long timeout, ProxyType proxyType) {
		return (T) getAsyncProxy(proxyType).buildProxy(t, timeout, true);
	}

	/**
	 * 
	 * 异步执行 AsyncCallback.doAsync方法
	 * 
	 * 
	 * @param @param asyncFuture
	 * @param @return
	 * @author guohao
	 * @date 2016年12月5日 下午4:45:00
	 * @since $
	 */
	public static <T> T execute(AsyncFuture<T> asyncFuture) {
		return execute(asyncFuture, AsyncConstant.ASYNC_DEFAULT_TIME_OUT);
	}

	
	/**
	 * 
	 * 
	 *  * <p>
	 * 
	 * 异步执行 AsyncCallback.doAsync方法
	 *
	 * </p>
	 * 
	 * @param AsyncCallback
	 *            <T> 需要实现的接口
	 * @param timeout
	 *            执行超时时间(单位：毫秒)
	 * @return T
	 *         必须带有返回参数且不支持void,array及Integer,Long,String,Boolean等Final修饰类</br>
	 *         如果需要返回以上类型，可以创建对象包装；如：
	 *         {@linkplain com.nuanshui.heatedloan.middleware.async.bean.AsyncResult}
	 * 
	 * 
	 * 
	 * @param @param asyncFuture
	 * @param @param timeout
	 * @param @return
	 * @author guohao
	 * @date 2016年12月5日 下午4:46:06
	 * @since $
	 */
	public static <T> T execute(AsyncFuture<T> asyncFuture, long timeout) {
		Type type = asyncFuture.getClass().getGenericInterfaces()[0];
		if (!(type instanceof ParameterizedType)) {
			throw new AsyncException(
					"you should specify AsyncCallback<T> for T type");
		}
		Class returnClass = ReflectionHelper.getGenericClass(
				(ParameterizedType) type, 0);
		return execute(asyncFuture, returnClass, timeout);
	}

	
	public static void execute(AsyncRunnable runable) {
		AsyncExecutor.submit(runable);
	}

	
	public static <T> void execute(final AsyncTaskFuture<T> asyncFutrue,
			AsyncFutureCallback<T> asyncFutureCallback) {

		AsyncExecutor.submit(new AsyncPoolCallable<T>() {
			@Override
			public T call() throws Exception {
				return asyncFutrue.doAsync();
			}
		}, asyncFutureCallback);
	}

	private static <T> T execute(final AsyncFuture<T> callback,
			Class<?> returnClass, long timeout) {
		Assert.notNull(callback);
		Assert.notNull(returnClass);
		if (Void.TYPE.isAssignableFrom(returnClass)) {
			return callback.doAsync();
		} else if (!Modifier.isPublic(returnClass.getModifiers())) {
			return callback.doAsync();
		} else if (Modifier.isFinal(returnClass.getModifiers())) {
			return callback.doAsync();
		} else if (returnClass.isPrimitive() || returnClass.isArray()) {
			return callback.doAsync();
		} else if (returnClass == Object.class) {
			return callback.doAsync();
		} else {
			AsyncFutureTask<T> future = AsyncExecutor
					.submit(new AsyncPoolCallable<T>() {
						@Override
						public T call() throws Exception {
							try {
								return callback.doAsync();
							} catch (Throwable e) {
								throw new AsyncException("future invoke error",e);
							}
						}
					});
			
			
			return (T) new AsyncResultProxy(future).buildProxy(returnClass,
					timeout, true);
		}
	}
}
